package com.weighing.system.service.impl.server;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weighing.common.annotation.DataSource;
import com.weighing.common.core.domain.entity.SysUser;
import com.weighing.common.enums.DataSourceType;
import com.weighing.common.utils.SecurityUtils;
import com.weighing.common.utils.StringUtils;
import com.weighing.system.domain.server.ProduceOrder;
import com.weighing.system.domain.server.ProduceOrderDetail;
import com.weighing.system.domain.server.WeighingDetail;
import com.weighing.system.domain.server.WeighingMaster;
import com.weighing.system.mapper.SysUserMapper;
import com.weighing.system.mapper.server.WeighingMasterMapper;
import com.weighing.system.service.server.ProduceOrderDetailService;
import com.weighing.system.service.server.ProduceOrderService;
import com.weighing.system.service.server.WeighingDetailService;
import com.weighing.system.service.server.WeighingMasterService;
import com.weighing.system.utils.SerialNumberUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
@DataSource(value = DataSourceType.SLAVE)
public class WeighingMasterServiceImpl extends ServiceImpl<WeighingMasterMapper, WeighingMaster> implements WeighingMasterService {

    @Resource
    private ProduceOrderService produceOrderService;

    @Resource
    private ProduceOrderDetailService produceOrderDetailService;

    @Resource
    private WeighingDetailService weighingDetailService;
    @Resource
    private SysUserMapper sysUserMapper;

    @Override
    public WeighingMaster getByBillNo005(String fromBillNo) {
        return baseMapper.selectOne(new LambdaQueryWrapper<>(WeighingMaster.class)
                .eq(WeighingMaster::getFromBillNo, fromBillNo)
                .eq(WeighingMaster::getIsFinished, false)
                .eq(WeighingMaster::getTypeId, "0005"));
    }

    @Override
    public WeighingMaster getByBillNo006(String fromBillNo) {
        return baseMapper.selectOne(new LambdaQueryWrapper<>(WeighingMaster.class)
                .eq(WeighingMaster::getFromBillNo, fromBillNo)
                .eq(WeighingMaster::getTypeId, "0006"));
    }

    @Override
    public Map<String, Object> checkWeighing(String billNo, String subMaterialId, Double weight) {
        Map<String, Object> map = new HashMap<>();
        map.put("upper", "/");
        map.put("lower", "/");
        map.put("recentlyQuantity", 0);
        if (StringUtils.isEmpty(billNo)) {
            map.put("flag", true);
            return map;
        }
        WeighingMaster weighingMaster = baseMapper.selectByBillNo(billNo);
        if (weighingMaster == null) {
            map.put("flag", true);
            return map;
        }
        // 本次辅料对应的produceOrderDetail
        ProduceOrderDetail produceOrderDetail = produceOrderDetailService.selectBySubMaterialIdAndBillNo(weighingMaster.getFromBillNo(), subMaterialId);
        if (produceOrderDetail.getBl() == null && !produceOrderDetail.getIsMainMaterial()) {
            throw new RuntimeException("辅料比例未维护");
        }
        // 主胶重量不需要校验
        if (produceOrderDetail.getIsMainMaterial()) {
            map.put("flag", true);
            return map;
        }
        List<WeighingDetail> details = weighingMaster.getDetails();
        // 完善detail的producDetail数据
        for (WeighingDetail detail : details) {
            detail.setProduceOrderDetail(produceOrderDetailService.selectBySubMaterialIdAndBillNo(weighingMaster.getFromBillNo(), detail.getCuMaterialId()));
        }
        List<WeighingDetail> collect = details.stream().filter(WeighingDetail::getCuIsMainMaterial).sorted(Comparator.comparing(WeighingDetail::getCreateTime).reversed()).collect(Collectors.toList());
        // 主料的累计称重
        WeighingDetail weighingDetail = collect.get(0);
        // 主胶的本次累计称重
        BigDecimal cumulativeQuantity = weighingDetail.getCumulativeQuantity();
        // 本次辅料称重 + 该辅料本单据历史称重重量
        List<WeighingDetail> weighingDetailList = weighingDetailService.getBaseMapper().selectList(new LambdaQueryWrapper<>(WeighingDetail.class)
                .eq(WeighingDetail::getCuMaterialId, subMaterialId)
                .eq(WeighingDetail::getBillNo, weighingMaster.getBillNo())
                .orderByDesc(WeighingDetail::getCreateTime));
        BigDecimal bigDecimal = weighingDetailList.isEmpty() ? BigDecimal.ZERO : weighingDetailList.get(0).getCumulativeQuantity();
        map.put("recentlyQuantity", bigDecimal.doubleValue());
        BigDecimal thisWeight = new BigDecimal(weight);
        // 比例
        BigDecimal bl = produceOrderDetail.getBl();
        // 浮动
        BigDecimal flu = produceOrderDetail.getFlu() == null ? BigDecimal.ZERO : produceOrderDetail.getFlu();
        BigDecimal quantity = cumulativeQuantity.multiply(bl.multiply(new BigDecimal("0.01"))).subtract(bigDecimal);
        BigDecimal upper = quantity.add(flu).setScale(3, RoundingMode.HALF_UP);
        BigDecimal lower = quantity.subtract(flu).setScale(3, RoundingMode.HALF_UP);
        // 是否在上限下限区间  包含上下限
        map.put("flag", thisWeight.compareTo(upper) <= 0 && thisWeight.compareTo(lower) >= 0);
        map.put("upper", upper.doubleValue() < 0 ? 0 : upper.doubleValue());
        map.put("lower", lower.doubleValue() < 0 ? 0 : lower.doubleValue());
        return map;
    }

    @Override
    public synchronized void addWeighingDetail(String billNo, String materialId, Double weight, String authorizerPersonId, String authorizerPersonName) {
        WeighingMaster weighingMaster = getByBillNo005(billNo);
        // 首次称重初始化称重单主表
        if (weighingMaster == null) {
            weighingMaster = new WeighingMaster();
            weighingMaster.setIsFinished(false);
            createWeighingMaster(weighingMaster, billNo);
            baseMapper.insert(weighingMaster);
        }
        // 添加称重明细
        createWeighingDetail(weighingMaster, materialId, weight, authorizerPersonId, authorizerPersonName);
    }

    private void createWeighingDetail(WeighingMaster weighingMaster, String materialId, Double weight, String authorizerPersonId, String authorizerPersonName) {
        ProduceOrderDetail produceOrderDetail = produceOrderDetailService.selectBySubMaterialIdAndBillNo(weighingMaster.getFromBillNo(), materialId);
        Long thisDate = SerialNumberUtil.generateSerialNumberLongSub8();
        int rowNo = 0;
        BigDecimal produceOrderTotalQuantity = BigDecimal.ZERO;
        BigDecimal totalQuantity = BigDecimal.ZERO;
        ProduceOrder produceOrder = produceOrderService.getByBillNo(weighingMaster.getFromBillNo());
        WeighingDetail weighingDetail = new WeighingDetail();
        weighingDetail.setBillNo(weighingMaster.getBillNo());
        weighingDetail.setAuthorizerPersonId(authorizerPersonId);
        weighingDetail.setAuthorizerPersonName(authorizerPersonName);
        weighingDetail.setRowCode(Math.abs(UUID.randomUUID().hashCode()));
        weighingDetail.setFromBillNo(weighingMaster.getFromBillNo());
        weighingDetail.setCuMaterialId(produceOrderDetail.getSubMaterialId());
        weighingDetail.setCuQuantity(BigDecimal.valueOf(weight));
        weighingDetail.setCuCzTime(thisDate);
        weighingDetail.setCuIsWeighing(true);
        weighingDetail.setCuIsMainMaterial(produceOrderDetail.getIsMainMaterial());
        weighingDetail.setCuBl(produceOrderDetail.getBl() == null ? null : produceOrderDetail.getBl());
        weighingDetail.setCreatorId(SecurityUtils.getUserId().toString());
        weighingDetail.setCreateTime(thisDate);
        weighingDetail.setLastOperatorId(SecurityUtils.getUserId().toString());
        weighingDetail.setLastOperateTime(thisDate);
        weighingDetail.setCreatorName(SecurityUtils.getUsername());
        if (produceOrder != null && (!produceOrder.getWeighingMasterList().isEmpty() || !produceOrder.getWeighingMasterListFinished().isEmpty())) {
            ArrayList<WeighingDetail> details = new ArrayList<>();
            if (produceOrder.getWeighingMasterList() != null && !produceOrder.getWeighingMasterList().isEmpty()) {
                if (produceOrder.getWeighingMasterList().get(0).getDetails() != null) {
                    for (WeighingMaster w : produceOrder.getWeighingMasterList()) {
                        details.addAll(w.getDetails());
                    }
                }
            }
            if (produceOrder.getWeighingMasterListFinished() != null && !produceOrder.getWeighingMasterListFinished().isEmpty()) {
                for (WeighingMaster w : produceOrder.getWeighingMasterListFinished()) {
                    details.addAll(w.getDetails());
                }
            }
            if (!details.isEmpty()) {
                if (produceOrder.getWeighingMasterList() != null && !produceOrder.getWeighingMasterList().isEmpty()) {
                    ArrayList<WeighingDetail> weighingDetails = new ArrayList<>();
                    for (WeighingMaster master : produceOrder.getWeighingMasterList()) {
                        weighingDetails.addAll(master.getDetails());
                    }
                    Integer maxRowNo = weighingDetails.stream()
                            .filter(detail -> detail.getCuMaterialId().equals(produceOrderDetail.getSubMaterialId()))
                            .map(WeighingDetail::getRowNo)
                            .max(Integer::compareTo)
                            .orElse(0); // 如果没有匹配结果，默认返回 0
                    rowNo = maxRowNo + 1;
                }
                // 工单累计称重
                BigDecimal maxTotalQuantity = details.stream()
                        .filter(detail -> detail.getCuMaterialId().equals(produceOrderDetail.getSubMaterialId()))
                        .map(WeighingDetail::getCuTotalQuantity)
                        .max(BigDecimal::compareTo)
                        .orElse(BigDecimal.ZERO); // 如果没有匹配结果，默认返回 0
                produceOrderTotalQuantity = maxTotalQuantity.add(BigDecimal.valueOf(weight));
                // 本次累计称重
                if (produceOrder.getWeighingMasterList() != null && !produceOrder.getWeighingMasterList().isEmpty()) {
                    ArrayList<WeighingDetail> thisWeighingDetails = new ArrayList<>();
                    for (WeighingMaster w : produceOrder.getWeighingMasterList()) {
                        thisWeighingDetails.addAll(w.getDetails());
                    }
                    BigDecimal maxThisQuantity = thisWeighingDetails.stream()
                            .filter(detail -> detail.getCuMaterialId().equals(produceOrderDetail.getSubMaterialId()))
                            .map(WeighingDetail::getCumulativeQuantity)
                            .max(BigDecimal::compareTo)
                            .orElse(BigDecimal.ZERO); // 如果没有匹配结果，默认返回 0
                    totalQuantity = maxThisQuantity.add(BigDecimal.valueOf(weight));
                }
            }
        }
        weighingDetail.setRowNo(rowNo);
        if (produceOrderTotalQuantity.equals(BigDecimal.ZERO)) {
            produceOrderTotalQuantity = BigDecimal.valueOf(weight);
        }
        if (totalQuantity.equals(BigDecimal.ZERO)) {
            totalQuantity = BigDecimal.valueOf(weight);
        }
        // 工单累计称重
        weighingDetail.setCuTotalQuantity(produceOrderTotalQuantity);
        // 本次累计称重
        weighingDetail.setCumulativeQuantity(totalQuantity);
        weighingDetailService.save(weighingDetail);
    }

    private void createWeighingMaster(WeighingMaster weighingMaster, String billNo) {
        Long thisDate = SerialNumberUtil.generateSerialNumberLongSub8();
        weighingMaster.setFromBillNo(billNo);
        String serialNumber = billNo + "-" + SerialNumberUtil.generateSerialNumber("yyyyMMddHHmmss");
        weighingMaster.setBillNo(serialNumber);
        weighingMaster.setBillDate(Long.valueOf(SerialNumberUtil.generateSerialNumber("yyyyMMdd")));
        weighingMaster.setTypeId("0005");
        weighingMaster.setCreatorId(SecurityUtils.getUserId().toString());
        weighingMaster.setCreateTime(thisDate);
        weighingMaster.setLastOperatorId(SecurityUtils.getUserId().toString());
        weighingMaster.setLastOperateTime(thisDate);
        weighingMaster.setPermitState((byte) 1);
    }

    @Override
    public WeighingMaster getWeighingMasterByBillNo(String billNo) {
        WeighingMaster weighingMaster = baseMapper.getWeighingMasterByBillNo(billNo);
        for (WeighingDetail detail : weighingMaster.getDetails()) {
            detail.setProduceOrderDetail(produceOrderDetailService.selectBySubMaterialIdAndBillNo(detail.getFromBillNo(), detail.getCuMaterialId()));
        }
        weighingMaster.setDetails(weighingMaster.getDetails().stream().sorted(Comparator.comparingLong(WeighingDetail::getCreateTime)).collect(Collectors.toList()));
        return weighingMaster;
    }

    @Override
    public void weighingMasterFinished(String billNo) {
        boolean update = new LambdaUpdateChainWrapper<>(baseMapper).eq(WeighingMaster::getBillNo, billNo).set(WeighingMaster::getIsFinished, true).update();
        if (!update) {
            throw new RuntimeException("更新称重单失败");
        }
        WeighingMaster weighingMaster = baseMapper.selectByBillNo(billNo);
        WeighingMaster weighingMaster006 = weighingMaster.clone();
        createWeighingMaster(weighingMaster006, weighingMaster006.getFromBillNo());
        weighingMaster006.setBillNo(weighingMaster.getBillNo() + "-0006");
        weighingMaster006.setTypeId("0006");
        weighingMaster006.setIsFinished(false);
        ArrayList<WeighingDetail> weighingDetails006 = new ArrayList<>();
        List<WeighingDetail> weighingDetailList = weighingMaster.getDetails();
        List<WeighingDetail> result = weighingDetailList.stream()
                // 按cuMaterialId分组
                .collect(Collectors.groupingBy(WeighingDetail::getCuMaterialId))
                // 对每个分组进行处理
                .values().stream()
                // 对每个分组按称重时间倒序排序，取第一条
                .map(group -> group.stream().max(Comparator.comparing(WeighingDetail::getCuCzTime))
                        .orElse(null))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!result.isEmpty()) {
            for (WeighingDetail weighingDetail : result) {
                weighingDetail.setBillNo(weighingMaster006.getBillNo());
                weighingDetail.setRowCode(Math.abs(UUID.randomUUID().hashCode()));
                weighingDetail.setCuXlTime(null);
                weighingDetail.setCreatorId(null);
                weighingDetail.setCreateTime(null);
                weighingDetail.setLastOperatorId(null);
                weighingDetail.setLastOperateTime(null);
                weighingDetail.setCreatorName(null);
                weighingDetails006.add(weighingDetail);
            }
            // 保存下料单
            baseMapper.insert(weighingMaster006);
            for (WeighingDetail weighingDetail : weighingDetails006) {
                weighingDetailService.getBaseMapper().insert(weighingDetail);
            }
        }
    }

    @Override
    public WeighingMaster getWeighingMaster006ByBillNo(String billNo) {
        WeighingMaster weighingMaster = baseMapper.getWeighingMaster006ByBillNo(billNo);
        if (weighingMaster == null) {
            throw new RuntimeException("未找到下料单");
        }
        weighingMaster.setProduceOrder(produceOrderService.getByBillNo(weighingMaster.getFromBillNo()));
        for (WeighingDetail detail : weighingMaster.getDetails()) {
            detail.setProduceOrderDetail(produceOrderDetailService.selectBySubMaterialIdAndBillNo(detail.getFromBillNo(), detail.getCuMaterialId()));
        }
        return weighingMaster;
    }

    @Override
    public void scanWeighingDetail(String billNo, Integer rowCode) {
        long thisTime = System.currentTimeMillis();
        Long thisDate = SerialNumberUtil.generateSerialNumberLongSub8();
        // 当前记录
        WeighingDetail weighingDetail = weighingDetailService.getBaseMapper().selectOne(new LambdaQueryWrapper<>(WeighingDetail.class)
                .eq(WeighingDetail::getBillNo, billNo)
                .eq(WeighingDetail::getRowCode, rowCode));
        List<WeighingDetail> weighingDetailList = weighingDetailService.getBaseMapper().selectList(new LambdaQueryWrapper<>(WeighingDetail.class)
                .eq(WeighingDetail::getBillNo, billNo).isNotNull(WeighingDetail::getCreatorId));
        if (weighingDetail == null) {
            throw new RuntimeException("未找到记录");
        }
        if (StringUtils.isNotEmpty(weighingDetail.getCreatorId())) {
            throw new RuntimeException("该记录已下料, 请勿重复操作");
        }
        if (!weighingDetailList.isEmpty()) {
            // 上一次下料时间
            Long recentlyCuXlTime = SerialNumberUtil.restoreOriginalTimestamp(weighingDetailList.stream().sorted(Comparator.comparing(WeighingDetail::getCreateTime).reversed()).collect(Collectors.toList()).get(0).getCuXlTime());
            WeighingDetail recentlyWeighingDetail = weighingDetailList.get(0);
            ProduceOrderDetail produceOrderDetail = produceOrderDetailService.getBaseMapper().selectOne(new LambdaQueryWrapper<>(ProduceOrderDetail.class)
                    .eq(ProduceOrderDetail::getBillNo, recentlyWeighingDetail.getFromBillNo())
                    .eq(ProduceOrderDetail::getSubMaterialId, recentlyWeighingDetail.getCuMaterialId()));
            if (produceOrderDetail != null && produceOrderDetail.getIntervalTime() != null) {
                if (thisTime - recentlyCuXlTime <= produceOrderDetail.getIntervalTime()) {
                    throw new RuntimeException("下料间隔时间为: " + produceOrderDetail.getIntervalTime() / 60000.0 + " 分钟, 请稍后再试");
                }
            }
        }
        weighingDetail.setCuCzTime(thisDate);
        weighingDetail.setCreatorId(SecurityUtils.getUserId().toString());
        weighingDetail.setCreateTime(thisDate);
        weighingDetail.setLastOperatorId(SecurityUtils.getUserId().toString());
        weighingDetail.setLastOperateTime(thisDate);
        weighingDetail.setCuXlTime(thisDate);
        weighingDetail.setCreatorName(SecurityUtils.getUsername());
        weighingDetailService.getBaseMapper().updateById(weighingDetail);
    }

    @Override
    @DataSource(value = DataSourceType.MASTER)
    public boolean matchesPassword(String userId, String rawPassword) {
        SysUser user = sysUserMapper.selectUserById(Long.parseLong(userId));
        return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
    }

}
